package com.welink.cmp.meatdata.importdata;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.welink.base.exception.BizException;
import com.welink.cmp.convertor.GenericConvertor;
import com.welink.cmp.domain.metadata.Host;
import com.welink.cmp.domain.metadata.Switch;
import com.welink.cmp.domain.metadata.gateway.IHostGateway;
import com.welink.cmp.domain.metadata.gateway.ISwitchGateway;
import com.welink.cmp.domain.topotree.DeviceTopologyTree;
import com.welink.cmp.topotree.command.NetTopologyCmdExe;
import com.welink.cmp.topotree.command.ServiceTopoExe;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author yanwei li create  2024-06-24 11:27
 */
@Service
@Slf4j
public class HostExcelListener implements ReadListener<HostExcelColumn> {

    private final static int BATCH_COUNT = 10;
    private List<HostExcelColumn> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    @Autowired
    private IHostGateway hostGateway;

    @Autowired
    private ServiceTopoExe serviceTopoExe;

    @Autowired
    private NetTopologyCmdExe netTopologyCmdExe;

    @Autowired
    private ISwitchGateway switchGateway;



    /**
     * @param data
     * @param context
     */
    @Override
    @Transactional
    public void invoke(HostExcelColumn data, AnalysisContext context) {
        log.info("解析到一条主机 Host 数据:{}", JSONObject.toJSONString(data));
        final Boolean canInsert = checkData(data);
        try{
            if (canInsert){
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }else {
                log.info("该条数据不符合导入条件，请手动处理后再次导入{}",JSONObject.toJSONString(data));
            }
        }catch (Exception e){
            cachedDataList.clear();
            throw new BizException("保存数据异常",e);
        }

    }

    /**
     * @param analysisContext
     */
    @Override
    @Transactional
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        saveData();
        log.info("所有数据解析完成！");
    }


    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据，开始存储数据库！", cachedDataList.size());
        final Boolean b = insertHostsChain();
        if (b){
            log.info("存储数据库成功！");
        }else{
            throw new BizException("导入主机数据失败，异常数据列表{"+ JSONObject.toJSONString(cachedDataList) +"}") ;
        }
    }

    private Boolean insertHostsChain(){
        // 1️⃣ 插入主机信息
        List<Host> hostList = GenericConvertor.toDOList(cachedDataList, Host.class);
        final Boolean hostSaveResult = hostGateway.saveOrUpdateBatchBySn(hostList);
        if (!hostSaveResult){
            cachedDataList.clear();
            throw new BizException("批量导入主机数据异常");
        }
        return insertServiceTree();
    }

    private Boolean insertServiceTree(){
        // 2️⃣ 插入服务树关系
        List<String> snList = cachedDataList.stream().map(HostExcelColumn::getSn).collect(Collectors.toList());
        final List<Host> hostListBySn = hostGateway.getHostListBySn(snList);
        Map<String,Long> snToHostMap = hostListBySn.stream().collect(Collectors.toMap(Host::getSn, Host::getId, (existing, replacement) -> existing));

        HashMap<Long,String> svcTreeMap = new HashMap<>(cachedDataList.size());
        for (HostExcelColumn hostExcelColumn : cachedDataList) {
            if (hostExcelColumn.getSvcNode()!=null){
                svcTreeMap.put(snToHostMap.get(hostExcelColumn.getSn()),hostExcelColumn.getSvcNode());
            }
        }
        if (!svcTreeMap.isEmpty()){
            final Boolean svcTreeBatch = serviceTopoExe.saveSvcTreeBatch(svcTreeMap);
        }
        return insertDeviceTopoTree(hostListBySn);
    }

    private Boolean insertDeviceTopoTree(List<Host> hostListBySn){
        // 3️⃣ 插入物理拓扑
        final Map<String, String> snToNameMap = hostListBySn.stream().collect(Collectors.toMap(Host::getSn, Host::getName,(sn,name)->sn));
        final List<String> ipList = cachedDataList.stream().map(HostExcelColumn::getSwitchIp).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ipList)){
            return Boolean.TRUE;
        }
        final List<Switch> switchListByIp = switchGateway.getSwitchListByIp(ipList);
        if (CollectionUtils.isEmpty(switchListByIp)){
            return Boolean.TRUE;
        }
        Map<String,String> ipToSwitchName = switchListByIp.stream().collect(Collectors.toMap(Switch::getManagementIp,Switch::getName,(ip,name)->ip));

        List<DeviceTopologyTree> deviceTopologyTreeList = new ArrayList<>();
        for (HostExcelColumn hostExcelColumn : cachedDataList) {
            if (StringUtils.isNotBlank(hostExcelColumn.getSwitchIp())){
                DeviceTopologyTree deviceTopologyTree = new DeviceTopologyTree();
                deviceTopologyTree.setLocalDevice(ipToSwitchName.get(hostExcelColumn.getSwitchIp()));
                deviceTopologyTree.setLocalInterface(hostExcelColumn.getSwitchInterface());
                deviceTopologyTree.setNeighborDevice(snToNameMap.get(hostExcelColumn.getSn()));
                deviceTopologyTreeList.add(deviceTopologyTree);
            }
        }
        return netTopologyCmdExe.saveOrUpdateBatch(deviceTopologyTreeList);
    }

    private Boolean checkData(HostExcelColumn data){

        if (data == null){
            return Boolean.FALSE;
        }
        if (StringUtils.isBlank(data.getSn())){
            return Boolean.FALSE;
        }
        if (StringUtils.isBlank(data.getName())){
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}
